package main

import (
	"bufio"
	"embed"
	"fmt"
	"io"
	"log"
	"math/rand"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/gdamore/tcell/v2"
)

// 嵌入chillhop.txt（无需额外携带）
//
//go:embed chillhop.txt
var embedFS embed.FS

// 核心配置
const (
	streamBaseURL   = "https://stream.chillhop.com/mp3/"
	toPlayFileName  = "to_play.mp3"         // 当前播放文件
	toCacheFileName = "to_cached.mp3"       // 后台缓存文件
	ffplayBin       = "ffplay"              // ffplay路径（可填绝对路径）
	logFileName     = "chillhop-player.log" // 调试日志文件
	paddingX        = 4                     // UI水平内边距
	paddingY        = 2                     // UI垂直内边距
)

// 圆角边框字符定义（核心缺失项）
const (
	topLeft     rune = '╭'
	topRight    rune = '╮'
	bottomLeft  rune = '╰'
	bottomRight rune = '╯'
	horizontal  rune = '─'
	vertical    rune = '│'
)

// Track 曲目信息
type Track struct {
	ID   string
	Name string
}

// 全局状态
var (
	tracks       []Track
	currentTrack *Track
	nextTrack    *Track
	isPlaying    bool
	isPaused     bool
	ffplayCmd    *exec.Cmd // ffplay进程句柄
	stopChan     = make(chan struct{})
	mu           sync.Mutex
	toPlayPath   string
	toCachePath  string
	logFile      *os.File
	// 全局样式（核心修复：颜色封装为Style）
	styleWhite  = tcell.StyleDefault.Foreground(tcell.ColorWhite).Background(tcell.ColorBlack)
	styleCyan   = tcell.StyleDefault.Foreground(tcell.ColorLightCyan).Background(tcell.ColorBlack)
	styleGreen  = tcell.StyleDefault.Foreground(tcell.ColorGreen).Background(tcell.ColorBlack)
	styleYellow = tcell.StyleDefault.Foreground(tcell.ColorYellow).Background(tcell.ColorBlack)
	styleGray   = tcell.StyleDefault.Foreground(tcell.ColorGray).Background(tcell.ColorBlack)
)

// 辅助函数：判断文件是否存在
func fileExists(path string) bool {
	_, err := os.Stat(path)
	return !os.IsNotExist(err)
}

// 初始化日志（写入当前目录，追加模式，带时间戳）
func initLogger() {
	file, err := os.OpenFile(logFileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		fmt.Printf("初始化日志失败: %v（不影响主功能）\n", err)
		return
	}
	logFile = file
	log.SetOutput(io.MultiWriter(os.Stdout, logFile))
	log.SetFlags(log.LstdFlags | log.Lmicroseconds)
	log.Println("=== 启动Chillhop播放器 ===")
}

func main() {
	initLogger()
	defer func() {
		if logFile != nil {
			log.Println("=== 退出Chillhop播放器 ===")
			logFile.Close()
		}
	}()

	rand.Seed(time.Now().UnixNano())

	// 1. 初始化临时文件路径
	initTempPaths()
	log.Printf("临时文件路径：to_play=%s, to_cached=%s", toPlayPath, toCachePath)
	defer cleanupTempFiles()

	// 2. 加载曲目
	if err := loadTracks(); err != nil {
		log.Printf("加载曲目失败: %v", err)
		fmt.Printf("加载曲目失败: %v\n", err)
		os.Exit(1)
	}
	if len(tracks) < 2 {
		log.Println("错误：chillhop.txt至少需要2首曲目（格式：ID!歌名）")
		fmt.Println("错误：chillhop.txt至少需要2首曲目（格式：ID!歌名）")
		os.Exit(1)
	}
	log.Printf("成功加载%d首曲目", len(tracks))

	// 3. 检查ffplay
	if err := checkFFplay(); err != nil {
		log.Printf("找不到ffplay: %v", err)
		fmt.Printf("错误：找不到ffplay（ffmpeg组件），请先安装ffmpeg：%v\n", err)
		os.Exit(1)
	}
	log.Println("ffplay检查通过")

	// 4. 初始化TUI
	screen, err := tcell.NewScreen()
	if err != nil {
		log.Printf("TUI初始化失败: %v", err)
		fmt.Printf("TUI初始化失败: %v\n", err)
		os.Exit(1)
	}
	if err := screen.Init(); err != nil {
		log.Printf("TUI启动失败: %v", err)
		fmt.Printf("TUI启动失败: %v\n", err)
		os.Exit(1)
	}
	defer func() {
		screen.Fini()
		// 重置终端
		if runtime.GOOS != "windows" {
			fmt.Print("\033[H\033[2J")
		} else {
			cmd := exec.Command("cmd", "/c", "cls")
			cmd.Stdout = os.Stdout
			cmd.Run()
		}
	}()

	screen.Clear()

	// 5. 启动核心流程
	go coreFlow(screen)

	// 6. 事件循环
	for {
		switch ev := screen.PollEvent().(type) {
		case *tcell.EventKey:
			switch {
			case ev.Key() == tcell.KeyEscape || ev.Key() == tcell.KeyCtrlC || ev.Rune() == 'q':
				cleanupAndExit()
				return
			case ev.Rune() == 's':
				skipTrack(screen)
				drawUI(screen)
			case ev.Rune() == 'p':
				togglePause(screen)
				drawUI(screen)
			}
		case *tcell.EventResize:
			screen.Sync()
			drawUI(screen)
		}
	}
}

// 以下是核心修复的关键函数：绘制圆角边框（修正SetContent参数）
func drawRoundedBorder(screen tcell.Screen, x1, y1, x2, y2 int) {
	// 四角（核心修复：传入正确的rune和Style）
	screen.SetContent(x1, y1, topLeft, nil, styleWhite)
	screen.SetContent(x2, y1, topRight, nil, styleWhite)
	screen.SetContent(x1, y2, bottomLeft, nil, styleWhite)
	screen.SetContent(x2, y2, bottomRight, nil, styleWhite)

	// 上下边框
	for x := x1 + 1; x < x2; x++ {
		screen.SetContent(x, y1, horizontal, nil, styleWhite)
		screen.SetContent(x, y2, horizontal, nil, styleWhite)
	}

	// 左右边框
	for y := y1 + 1; y < y2; y++ {
		screen.SetContent(x1, y, vertical, nil, styleWhite)
		screen.SetContent(x2, y, vertical, nil, styleWhite)
	}
}

// 绘制文本（修正样式参数）
func drawText(screen tcell.Screen, x, y int, text string, style tcell.Style) {
	for i, r := range text {
		screen.SetContent(x+i, y, r, nil, style)
	}
}

// 绘制分割线（修正样式）
func drawDivider(screen tcell.Screen, x, y, width int, char rune) {
	for i := 0; i < width; i++ {
		screen.SetContent(x+i, y, char, nil, styleGray)
	}
}

// 显示状态信息（修正样式）
func drawStatus(screen tcell.Screen, msg string) {
	w, _ := screen.Size()
	statusY := paddingY + 6
	statusX := (w - len(msg)) / 2
	drawText(screen, statusX, statusY, msg, styleWhite)
	screen.Sync()
}

// 绘制美化版UI（使用修复后的绘图函数）
func drawUI(screen tcell.Screen) {
	screen.Clear()
	w, h := screen.Size()

	// 1. 绘制圆角边框（已修复）
	drawRoundedBorder(screen, 0, 0, w-1, h-1)

	// 2. 标题
	title := "🎵 Chillhop 播放器（FFplay版）"
	titleX := (w - len(title)) / 2
	drawText(screen, titleX, paddingY, title, styleCyan)

	// 3. 曲目信息
	infoY := paddingY + 2
	if currentTrack != nil {
		currentText := fmt.Sprintf("📌 当前曲目: %s", truncate(currentTrack.Name, w-paddingX*2-10))
		drawText(screen, paddingX, infoY, currentText, styleGreen)
		nextText := fmt.Sprintf("📌 下首曲目: %s", truncate(nextTrack.Name, w-paddingX*2-10))
		drawText(screen, paddingX, infoY+1, nextText, styleYellow)
	} else {
		drawText(screen, paddingX, infoY, "📌 加载中...", styleGray)
	}

	// 4. 状态
	statusY := infoY + 3
	statusText := getCurrentStatus()
	statusX := (w - len(statusText)) / 2
	drawText(screen, statusX, statusY, statusText, styleWhite)

	// 5. 分割线
	dividerY := statusY + 2
	drawDivider(screen, paddingX, dividerY, w-paddingX-1, '-')

	// 6. 帮助信息
	helpY := dividerY + 2
	helps := []string{
		"🖱️  操作说明：",
		"  p: 暂停/继续播放",
		"  s: 跳过当前曲目",
		"  q/ESC/Ctrl+C: 退出程序",
		"📝 日志文件：当前目录/chillhop-player.log",
	}
	for i, help := range helps {
		drawText(screen, paddingX, helpY+i, help, styleGray)
	}

	screen.Sync()
}

// 以下是未修改的原有功能函数（保持不变）
func initTempPaths() {
	tempDir := os.TempDir()
	toPlayPath = filepath.Join(tempDir, toPlayFileName)
	toCachePath = filepath.Join(tempDir, toCacheFileName)
}

func cleanupTempFiles() {
	if fileExists(toPlayPath) {
		if err := os.Remove(toPlayPath); err != nil {
			log.Printf("删除to_play.mp3失败: %v", err)
		} else {
			log.Println("已删除to_play.mp3")
		}
	}
	if fileExists(toCachePath) {
		if err := os.Remove(toCachePath); err != nil {
			log.Printf("删除to_cached.mp3失败: %v", err)
		} else {
			log.Println("已删除to_cached.mp3")
		}
	}
}

func loadTracks() error {
	file, err := embedFS.Open("chillhop.txt")
	if err != nil {
		return err
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if line != "" && strings.Contains(line, "!") {
			parts := strings.SplitN(line, "!", 2)
			tracks = append(tracks, Track{ID: parts[0], Name: parts[1]})
			log.Printf("加载曲目：ID=%s, 名称=%s", parts[0], parts[1])
		}
	}
	return scanner.Err()
}

func checkFFplay() error {
	cmd := exec.Command(ffplayBin, "-version")
	return cmd.Run()
}

func coreFlow(screen tcell.Screen) {
	for {
		selectTracks()

		if !fileExists(toPlayPath) {
			drawStatus(screen, fmt.Sprintf("📥 下载播放文件: %s", currentTrack.Name))
			log.Printf("开始下载播放文件：ID=%s, 名称=%s", currentTrack.ID, currentTrack.Name)
			if err := downloadTrack(currentTrack.ID, toPlayPath, screen); err != nil {
				errMsg := fmt.Sprintf("下载失败: %v，重试中...", err)
				drawStatus(screen, errMsg)
				log.Printf("下载播放文件失败: %v", err)
				time.Sleep(2 * time.Second)
				continue
			}
			log.Printf("下载播放文件完成：%s", toPlayPath)
		}

		go func() {
			if !fileExists(toCachePath) {
				drawStatus(screen, fmt.Sprintf("▶️  播放: %s | 📥 缓存: %s", currentTrack.Name, nextTrack.Name))
				log.Printf("开始缓存下一首：ID=%s, 名称=%s", nextTrack.ID, nextTrack.Name)
				if err := downloadTrack(nextTrack.ID, toCachePath, screen); err != nil {
					errMsg := fmt.Sprintf("▶️  播放: %s | ❌ 缓存失败: %v", currentTrack.Name, err)
					drawStatus(screen, errMsg)
					log.Printf("缓存下一首失败: %v", err)
				} else {
					succMsg := fmt.Sprintf("▶️  播放: %s | ✅ 缓存完成", currentTrack.Name)
					drawStatus(screen, succMsg)
					log.Printf("缓存下一首完成：%s", toCachePath)
				}
			}
		}()

		playMsg := fmt.Sprintf("▶️  正在播放: %s", currentTrack.Name)
		drawStatus(screen, playMsg)
		log.Printf("开始播放：%s", currentTrack.Name)
		playWithFFplay(screen)

		switchFiles()
		log.Println("播放完成，切换到下一首")
	}
}

func selectTracks() {
	mu.Lock()
	defer mu.Unlock()

	if currentTrack == nil || nextTrack == nil {
		idx1, idx2 := rand.Intn(len(tracks)), rand.Intn(len(tracks))
		for idx2 == idx1 {
			idx2 = rand.Intn(len(tracks))
		}
		currentTrack = &tracks[idx1]
		nextTrack = &tracks[idx2]
		log.Printf("首次选择曲目：当前=%s(ID:%s), 下一首=%s(ID:%s)",
			currentTrack.Name, currentTrack.ID, nextTrack.Name, nextTrack.ID)
		return
	}

	// prevCurrent := currentTrack
	currentTrack = nextTrack
	for {
		newIdx := rand.Intn(len(tracks))
		if tracks[newIdx].ID != currentTrack.ID {
			nextTrack = &tracks[newIdx]
			break
		}
	}
	log.Printf("切换曲目：当前=%s(ID:%s), 下一首=%s(ID:%s)",
		currentTrack.Name, currentTrack.ID, nextTrack.Name, nextTrack.ID)
}

func downloadTrack(trackID, savePath string, screen tcell.Screen) error {
	resp, err := http.Get(fmt.Sprintf("%s%s", streamBaseURL, trackID))
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("HTTP %d", resp.StatusCode)
	}

	file, err := os.Create(savePath)
	if err != nil {
		return err
	}
	defer file.Close()

	_, err = io.Copy(file, resp.Body)
	return err
}

func playWithFFplay(screen tcell.Screen) {
	mu.Lock()
	isPlaying = true
	isPaused = false
	mu.Unlock()

	cmd := exec.Command(ffplayBin, "-nodisp", "-autoexit", "-volume", "80", toPlayPath)
	ffplayCmd = cmd

	if err := cmd.Start(); err != nil {
		errMsg := fmt.Sprintf("播放失败: %v", err)
		drawStatus(screen, errMsg)
		log.Printf("%s", errMsg)
		mu.Lock()
		isPlaying = false
		mu.Unlock()
		return
	}
	log.Printf("ffplay进程启动：PID=%d", cmd.Process.Pid)

	go func() {
		cmd.Wait()
		mu.Lock()
		if !isPaused {
			isPlaying = false
			log.Printf("ffplay进程结束：PID=%d（正常播放完成）", cmd.Process.Pid)
		} else {
			log.Printf("ffplay进程结束：PID=%d（暂停终止）", cmd.Process.Pid)
		}
		mu.Unlock()
	}()

	for {
		mu.Lock()
		playing := isPlaying
		mu.Unlock()
		if !playing {
			break
		}
		time.Sleep(100 * time.Millisecond)
	}
}

func switchFiles() {
	mu.Lock()
	defer mu.Unlock()

	if fileExists(toCachePath) {
		if fileExists(toPlayPath) {
			if err := os.Remove(toPlayPath); err != nil {
				log.Printf("删除旧to_play.mp3失败: %v", err)
			}
		}
		if err := os.Rename(toCachePath, toPlayPath); err != nil {
			log.Printf("切换文件失败: %v", err)
		} else {
			log.Printf("文件切换成功：to_cached.mp3 → to_play.mp3")
		}
	} else {
		log.Println("切换文件失败：to_cached.mp3不存在")
	}
}

func skipTrack(screen tcell.Screen) {
	mu.Lock()
	defer mu.Unlock()

	if isPlaying && ffplayCmd != nil && ffplayCmd.Process != nil {
		log.Printf("跳过当前曲目，终止ffplay进程：PID=%d", ffplayCmd.Process.Pid)
		ffplayCmd.Process.Kill()
	}

	cleanupTempFiles()
	currentTrack = nil
	nextTrack = nil
	isPlaying = false
	isPaused = false
	ffplayCmd = nil
	statusMsg := "⏭️  已跳过，重新加载中..."
	drawStatus(screen, statusMsg)
	log.Println(statusMsg)
}

func togglePause(screen tcell.Screen) {
	mu.Lock()
	defer mu.Unlock()

	if !isPlaying || ffplayCmd == nil || ffplayCmd.Process == nil {
		log.Println("暂停/继续失败：无正在播放的进程")
		return
	}

	switch runtime.GOOS {
	case "windows":
		if isPaused {
			cmd := exec.Command(ffplayBin, "-nodisp", "-autoexit", "-volume", "80", toPlayPath)
			ffplayCmd = cmd
			if err := cmd.Start(); err != nil {
				errMsg := fmt.Sprintf("▶️  继续失败: %v", err)
				drawStatus(screen, errMsg)
				log.Printf(errMsg)
				return
			}
			log.Printf("Windows继续播放，重启ffplay进程：PID=%d", cmd.Process.Pid)
			go func() {
				cmd.Wait()
				mu.Lock()
				if !isPaused {
					isPlaying = false
					log.Printf("ffplay进程结束：PID=%d", cmd.Process.Pid)
				}
				mu.Unlock()
			}()
			isPaused = false
			statusMsg := fmt.Sprintf("▶️  继续播放: %s", currentTrack.Name)
			drawStatus(screen, statusMsg)
			log.Println(statusMsg)
		} else {
			log.Printf("Windows暂停播放，终止ffplay进程：PID=%d", ffplayCmd.Process.Pid)
			ffplayCmd.Process.Kill()
			isPaused = true
			isPlaying = true
			statusMsg := fmt.Sprintf("⏸️  已暂停: %s（Windows下继续将从头播放）", currentTrack.Name)
			drawStatus(screen, statusMsg)
			log.Println(statusMsg)
		}
	default:
		if isPaused {
			ffplayCmd.Process.Signal(syscall.SIGCONT)
			isPaused = false
			statusMsg := fmt.Sprintf("▶️  继续播放: %s", currentTrack.Name)
			drawStatus(screen, statusMsg)
			log.Println(statusMsg)
		} else {
			ffplayCmd.Process.Signal(syscall.SIGSTOP)
			isPaused = true
			statusMsg := fmt.Sprintf("⏸️  已暂停: %s", currentTrack.Name)
			drawStatus(screen, statusMsg)
			log.Println(statusMsg)
		}
	}
}

func cleanupAndExit() {
	mu.Lock()
	defer mu.Unlock()

	if ffplayCmd != nil && ffplayCmd.Process != nil {
		log.Printf("退出时终止ffplay进程：PID=%d", ffplayCmd.Process.Pid)
		ffplayCmd.Process.Kill()
	}

	cleanupTempFiles()

	log.Println("退出成功，已清理所有文件")
	fmt.Println("\n===== 退出成功，已清理所有文件 =====")
	os.Exit(0)
}

func getCurrentStatus() string {
	mu.Lock()
	defer mu.Unlock()

	if isPlaying {
		if isPaused {
			return fmt.Sprintf("⏸️  已暂停 | %s", currentTrack.Name)
		} else {
			return fmt.Sprintf("▶️  播放中 | %s", currentTrack.Name)
		}
	} else if currentTrack != nil {
		return "📥 下载中..."
	} else {
		return "⏳ 准备中..."
	}
}

func truncate(s string, maxLen int) string {
	if len(s) <= maxLen {
		return s
	}
	return s[:maxLen-3] + "..."
}
